//-------------------------------------------------------------------
//	Copyright (c) 2010-2011  Zhirnov Andrey
//	This file is part of the "UXGEN_ResourceConverter" project.
//	See copyright notice in "Converter.h".
//-------------------------------------------------------------------

#include "MeshConverters.h"
#include "..\\..\\Core\\CConverter.h"



//--------------------- Change Mesh Type --------------------------//
/*
=================================================
	
=================================================
*/
bool SceletalMeshToMultiMesh(TMesh &sMesh, const TMeshAnimation &sAnim, const uint32_array_t &aTracks)
{
	return false;
}



//---------------------- TBN Generation ---------------------------//
/*
=================================================
	
=================================================
*/
typedef vec3 (*PConvertVertex3Proc_t)(const void *);
typedef vec2 (*PConvertVertex2Proc_t)(const void *);


template <typename T>
vec3 ConvertVertex3(const void *pVert)
{
	const T *	p_vert = (const T *)pVert;
	return vec3( float(p_vert[0]), float(p_vert[1]), float(p_vert[2]) );
}

vec3 DefaultConvertVertex3(const void *)
{
	return vec3();
}


template <typename T>
vec2 ConvertVertex2(const void *pVert)
{
	const T *	p_vert = (const T *)pVert;
	return vec2( float(p_vert[0]), float(p_vert[1]) );
}

vec2 DefaultConvertVertex2(const void *)
{
	return vec2();
}

/*
=================================================
	
=================================================
*/
PConvertVertex3Proc_t GetConvertVertex3Proc(gl_vertex::type eType)
{
	switch ( eType )
	{
		case gl_vertex::BYTE	:	return & ConvertVertex3< int8    >;
		case gl_vertex::UBYTE	:	return & ConvertVertex3< uint8   >;
		case gl_vertex::SHORT	:	return & ConvertVertex3< int16   >;
		case gl_vertex::USHORT	:	return & ConvertVertex3< uint16  >;
		case gl_vertex::INT		:	return & ConvertVertex3< int32   >;
		case gl_vertex::UINT	:	return & ConvertVertex3< uint32  >;
		case gl_vertex::HALF	:	return & ConvertVertex3< half_t  >;
		case gl_vertex::FLOAT	:	return & ConvertVertex3< float   >;
		case gl_vertex::DOUBLE	:	return & ConvertVertex3< double  >;
		case gl_vertex::FIXED	:	return & ConvertVertex3< fixed_t >;
		/*
		case gl_vertex::INT_2_10_10_10_REV			:
		case gl_vertex::UNSIGNED_INT_2_10_10_10_REV	:
		*/
		default					:	return & DefaultConvertVertex3;
	};
}

PConvertVertex2Proc_t GetConvertVertex2Proc(gl_vertex::type eType)
{
	switch ( eType )
	{
		case gl_vertex::BYTE	:	return & ConvertVertex2< int8    >;
		case gl_vertex::UBYTE	:	return & ConvertVertex2< uint8   >;
		case gl_vertex::SHORT	:	return & ConvertVertex2< int16   >;
		case gl_vertex::USHORT	:	return & ConvertVertex2< uint16  >;
		case gl_vertex::INT		:	return & ConvertVertex2< int32   >;
		case gl_vertex::UINT	:	return & ConvertVertex2< uint32  >;
		case gl_vertex::HALF	:	return & ConvertVertex2< half_t  >;
		case gl_vertex::FLOAT	:	return & ConvertVertex2< float   >;
		case gl_vertex::DOUBLE	:	return & ConvertVertex2< double  >;
		case gl_vertex::FIXED	:	return & ConvertVertex2< fixed_t >;
		/*
		case gl_vertex::INT_2_10_10_10_REV			:
		case gl_vertex::UNSIGNED_INT_2_10_10_10_REV	:
		*/
		default					:	return & DefaultConvertVertex2;
	};
}

/*
=================================================
	
=================================================
*/
void GenerateTBN(const vec3 &vert0,	const vec3 &vert1,	const vec3 &vert2,
				 const vec2 &tex0,	const vec2 &tex1,	const vec2 &tex2,
				 float **normal,	float **tangent,	float **binormal)
{
	vec3	v_normal = Cross( vert1 - vert0, vert2 - vert0 ),
			v_tangent,
			v_binormal;

	v_normal.Normalize();


	vec3		e0( vert1.x - vert0.x, tex1.x - tex0.x, tex1.y - tex0.y ),
				e1( vert2.x - vert0.x, tex2.x - tex0.x, tex2.y - tex0.y ),
				cp = Cross( e0, e1 );

	if ( !IsZero( cp.x ) ) {
		v_tangent.x  = -cp.y / cp.x;
		v_binormal.x = -cp.z / cp.x;
	}
	else {
		v_tangent.x  = 0.f;
		v_binormal.x = 0.f;
	}

	e0.x = vert1.y - vert0.y;
	e1.x = vert2.y - vert0.y;
	cp   = Cross( e0, e1 );

	if ( !IsZero( cp.x ) ) {
		v_tangent.y  = -cp.y / cp.x;
		v_binormal.y = -cp.z / cp.x;
	}
	else {
		v_tangent.y  = 0.f;
		v_binormal.y = 0.f;
	}

	e0.x = vert1.z - vert0.z;
	e1.x = vert2.z - vert0.z;
	cp   = Cross( e0, e1 );

	if ( !IsZero( cp.x ) ) {
		v_tangent.z  = -cp.y / cp.x;
		v_binormal.z = -cp.z / cp.x;
	}
	else {
		v_tangent.z  = 0.f;
		v_binormal.z = 0.f;
	}

	if ( Dot( Cross( v_tangent, v_binormal ), v_normal ) < 0.f )
		v_tangent = -v_tangent;


	if ( normal != nullptr )
	{
		memcpy( normal[0],   v_normal.ptr(),   sizeof(v_normal) );
		memcpy( normal[1],   v_normal.ptr(),   sizeof(v_normal) );
		memcpy( normal[2],   v_normal.ptr(),   sizeof(v_normal) );
	}

	if ( tangent != nullptr )
	{
		memcpy( tangent[0],  v_tangent.ptr(),  sizeof(v_tangent) );
		memcpy( tangent[1],  v_tangent.ptr(),  sizeof(v_tangent) );
		memcpy( tangent[2],  v_tangent.ptr(),  sizeof(v_tangent) );
	}

	if ( binormal != nullptr )
	{
		memcpy( binormal[0], v_binormal.ptr(), sizeof(v_binormal) );
		memcpy( binormal[1], v_binormal.ptr(), sizeof(v_binormal) );
		memcpy( binormal[2], v_binormal.ptr(), sizeof(v_binormal) );
	}
}

/*
=================================================
	
=================================================
*/
template <typename T>
bool GenerateSubMeshTBN(TMultiMesh::TSubMesh &sSubMesh,
						const binary_buffer_t &aVertices, const T *pIndices,
						binary_buffer_t &aNewVertices, binary_buffer_t &aNewIndices,
						bool bTangent, bool bBinormal, bool bNormal)
{
	struct TVertAttribParams
	{
		const void *	pVertices;
		uint			uSizeOf;
		uint			uStride;
		///
		TVertAttribParams(): pVertices(nullptr), uSizeOf(0), uStride(0) {}
	};
	typedef array< TVertAttribParams >	vaparams_array_t;
	

	vaparams_array_t	a_vaparams;
	const uint			u_tbn_size	= ((bTangent ? 3 : 0) + (bBinormal ? 3 : 0) + (bNormal ? 3 : 0)) * sizeof(float);
	binary_buffer_t		a_vertex;
	uint				u_vert_size	= u_tbn_size;
	uint				q			= 0;

	int					a_offsets_rev[3] = {0};	// [normal][tangent][binormal]

	PConvertVertex3Proc_t	p_pos_conv	= nullptr;
	PConvertVertex2Proc_t	p_texc_conv	= nullptr;


	a_offsets_rev[2] = (bBinormal ? -3 : 0) * sizeof(float);
	a_offsets_rev[1] = a_offsets_rev[2] + (bTangent ? -3 : 0) * sizeof(float);
	a_offsets_rev[0] = a_offsets_rev[1] + (bNormal  ? -3 : 0) * sizeof(float);

	
	a_vaparams.Resize( sSubMesh.aAttribs.Count(), false );
	
 
	for (uint i = 0; i < sSubMesh.aAttribs.Count(); ++i)
	{
		TVertAttribParams &	s_params = a_vaparams[i];

		s_params.uSizeOf	= GetVertexSize( sSubMesh.aAttribs[i].eType) * sSubMesh.aAttribs[i].uCount;
		s_params.uStride	= sSubMesh.aAttribs[i].uStride == 0 ? s_params.uSizeOf : sSubMesh.aAttribs[i].uStride;
		s_params.pVertices	= cptr_move( aVertices.ptr(), sSubMesh.aAttribs[i].uOffset );

		u_vert_size			+= s_params.uSizeOf;

		if ( sSubMesh.aAttribs[i].eLocation == e_vertex_attrib::POSITION && sSubMesh.aAttribs[i].uCount == 3 )
			p_pos_conv = GetConvertVertex3Proc( sSubMesh.aAttribs[i].eType );

		if ( sSubMesh.aAttribs[i].eLocation == e_vertex_attrib::TEXCOORD_0 && sSubMesh.aAttribs[i].uCount == 2 )
			p_texc_conv = GetConvertVertex2Proc( sSubMesh.aAttribs[i].eType );
	}

	if ( p_pos_conv == nullptr || p_texc_conv == nullptr ) {
		LOG( "can't find position or texcoords vertex attributes", );
		return false;
	}


	a_vertex.Resize( u_vert_size, false );
	sSubMesh.uIdxOffset = (uint)aNewIndices.Size();


	for (uint i = 0; i < sSubMesh.uIdxCount; ++i)
	{
		typedef const void *	pcvoid_t;
		typedef float *			pfloat_t;

		pcvoid_t	a_positions[3]	= {0};
		pcvoid_t	a_texcoords[3]	= {0};
		pfloat_t	a_normals[3]	= {0};
		pfloat_t	a_tangents[3]	= {0};
		pfloat_t	a_binormals[3]	= {0};


		for (uint j = 0; j < sSubMesh.aAttribs.Count(); ++j)
		{
			const void * p_vert = cptr_move( a_vaparams[j].pVertices, pIndices[i] * a_vaparams[j].uStride );
			memcpy( a_vertex.ptr(), p_vert, a_vaparams[j].uSizeOf );

			if ( sSubMesh.aAttribs[j].eLocation == e_vertex_attrib::POSITION )		a_positions[q] = p_vert;	else
			if ( sSubMesh.aAttribs[j].eLocation == e_vertex_attrib::TEXCOORD_0 )	a_texcoords[q] = p_vert;
		}

		aNewVertices.Append( a_vertex );
		aNewIndices.Append( &i, (&i) + 1 );

		a_normals[q]	= ptr_move<float *>( aNewVertices.ptr(), a_offsets_rev[0] );
		a_tangents[q]	= ptr_move<float *>( aNewVertices.ptr(), a_offsets_rev[1] );
		a_binormals[q]	= ptr_move<float *>( aNewVertices.ptr(), a_offsets_rev[2] );


		if ( ++q == 3 )
		{
			q = 0;
			GenerateTBN( p_pos_conv( a_positions[0] ),  p_pos_conv( a_positions[1] ),  p_pos_conv( a_positions[2] ),
						 p_texc_conv( a_texcoords[0] ), p_texc_conv( a_texcoords[1] ), p_texc_conv( a_texcoords[2] ),
						 bNormal ? a_normals : nullptr,
						 bTangent ? a_tangents : nullptr,
						 bBinormal ? a_binormals : nullptr );
		}
	}

	sSubMesh.eType = gl_index::UINT;
	
	if ( bTangent )
		sSubMesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::TANGENT, 3, 0, 0, gl_vertex::FLOAT, false, 0 ) );

	if ( bBinormal )
		sSubMesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::BINORMAL, 3, 0, 0, gl_vertex::FLOAT, false, 0 ) );

	if ( bNormal )
		sSubMesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::NORMAL, 3, 0, 0, gl_vertex::FLOAT, false, 0 ) );

	return true;
}

/*
=================================================
	
=================================================
*/
bool GenerateMultiMeshTBN(TMultiMesh &sMesh, bool bTangent, bool bBinormal, bool bNormal)
{
	binary_buffer_t		a_vertices,
						a_indices;

	for (uint i = 0; i < sMesh.aSubMeshes.Count(); ++i)
	{
		const void * p_indices	= cptr_move( sMesh.aIndices.ptr(), sMesh.aSubMeshes[i].uIdxOffset );
		bool		 b_res		= false;

		switch ( sMesh.aSubMeshes[i].eType )
		{
			case gl_index::UBYTE :
				b_res = GenerateSubMeshTBN<uint8>( sMesh.aSubMeshes[i], sMesh.aVertices, (const uint8 *)p_indices,
													a_vertices, a_indices, bTangent, bBinormal, bNormal );
				break;
				
			case gl_index::USHORT :
				b_res = GenerateSubMeshTBN<uint16>( sMesh.aSubMeshes[i], sMesh.aVertices, (const uint16 *)p_indices,
													a_vertices, a_indices, bTangent, bBinormal, bNormal );
				break;
				
			case gl_index::UINT :
				b_res = GenerateSubMeshTBN<uint32>( sMesh.aSubMeshes[i], sMesh.aVertices, (const uint32 *)p_indices,
													a_vertices, a_indices, bTangent, bBinormal, bNormal );
				break;
		};

		if ( !b_res )
			return false;
	}

	sMesh.aIndices.SetMem( a_indices );
	sMesh.aVertices.SetMem( a_vertices );

	return true;
}

/*
=================================================
	
=================================================
*/
bool GenerateSceletalMeshTBN(TSceletalMesh &sMesh, bool bTangent, bool bBinormal, bool bNormal)
{
	LOG( "can't generate TBN for sceletal mesh", );
	return false;
}

/*
=================================================
	
=================================================
*/
bool GenerateTBN(TMesh &sMesh, bool bTangent, bool bBinormal, bool bNormal)
{
	switch ( sMesh.eType )
	{
		case TMesh::MULTI_MESH :
			return GenerateMultiMeshTBN( *sMesh.sData.pMesh, bTangent, bBinormal, bNormal );

		case TMesh::SCELETAL_MESH :
			return GenerateSceletalMeshTBN( *sMesh.sData.pScelet, bTangent, bBinormal, bNormal );
	};
	return false;
}



//---------------- Vertex Attribs Modification --------------------//
/*
=================================================
	
=================================================
*/
template <typename T1, typename T2>	inline T1		TypeCast(const T2 &val)			{ return T1( val ); }
template <typename T>				inline T		TypeCast(const fixed_t &val)	{ return T( val.GetInt() ); }
template <>							inline half_t	TypeCast(const fixed_t &val)	{ return val.GetFloat(); }
template <>							inline float	TypeCast(const fixed_t &val)	{ return val.GetFloat(); }
template <>							inline double	TypeCast(const fixed_t &val)	{ return val.GetFloat(); }
template <typename T>				inline T		TypeCast(const half_t &val)		{ return T( val.GetI() ); }
template <>							inline fixed_t	TypeCast(const half_t &val)		{ return val.Get(); }
template <>							inline float	TypeCast(const half_t &val)		{ return val.Get(); }
template <>							inline double	TypeCast(const half_t &val)		{ return val.Get(); }
template <typename T>				inline T		TypeCast(const uint32 &val)		{ return T( val ); }
template <>							inline fixed_t	TypeCast(const uint32 &val)		{ return fixed_t( int(val) ); }

/*
=================================================
	
=================================================
*/
typedef void (*PChangeVertTypeProc_t)(void *, const void *, float, uint);


template <typename T1, typename T2>
void ChangeVertType(void *pTo, const void *pFrom, float fScale, uint uCount)
{
	T1		 *	p_vert1 = (T1 *)pTo;
	T2 const *	p_vert2 = (T2 const *)pFrom;

	for (uint i = 0; i < uCount; ++i)
		p_vert1[i] = TypeCast<T1>( p_vert2[i] );
}


template <typename T1, typename T2>
void ChangeVertTypeNorm(void *pTo, const void *pFrom, float fScale, uint uCount)
{
	T1		 *	p_vert1 = (T1 *)pTo;
	T2 const *	p_vert2 = (T2 const *)pFrom;

	for (uint i = 0; i < uCount; ++i)
		p_vert1[i] = TypeCast<T1>( p_vert2[i] ) / TypeCast<T1>( MaxValue<T2>() );
}


template <typename T1, typename T2>
void ChangeVertTypeScale(void *pTo, const void *pFrom, float fScale, uint uCount)
{
	T1		 *	p_vert1 = (T1 *)pTo;
	T2 const *	p_vert2 = (T2 const *)pFrom;

	for (uint i = 0; i < uCount; ++i)
		p_vert1[i] = TypeCast<T1>( p_vert2[i] * fScale );
}


template <typename T1, typename T2>
void ChangeVertTypeScaleNorm(void *pTo, const void *pFrom, float fScale, uint uCount)
{
	T1		 *	p_vert1 = (T1 *)pTo;
	T2 const *	p_vert2 = (T2 const *)pFrom;

	for (uint i = 0; i < uCount; ++i)
		p_vert1[i] = TypeCast<T1>( p_vert2[i] * fScale ) / TypeCast<T1>( MaxValue<T2>() );
}


void DefaultChangeVertType(void *pTo, const void *pFrom, float fScale, uint uCount)
{
}

/*
=================================================
	
=================================================
*/
uint GetVertexTypeIndex(gl_vertex::type eType)
{
	switch ( eType )
	{
		case gl_vertex::BYTE	:	return 1;
		case gl_vertex::UBYTE	:	return 2;
		case gl_vertex::SHORT	:	return 3;
		case gl_vertex::USHORT	:	return 4;
		case gl_vertex::INT		:	return 5;
		case gl_vertex::UINT	:	return 6;
		case gl_vertex::HALF	:	return 7;
		case gl_vertex::FLOAT	:	return 8;
		case gl_vertex::DOUBLE	:	return 9;
		case gl_vertex::FIXED	:	return 10;
		/*
		case gl_vertex::INT_2_10_10_10_REV			:	return 11;
		case gl_vertex::UNSIGNED_INT_2_10_10_10_REV	:	return 12;
		*/
		default					:	return 0;
	};
}
	
/*
=================================================
	
=================================================
*/
PChangeVertTypeProc_t GetChangeVertTypeProc(gl_vertex::type eTo, gl_vertex::type eFrom, bool bScale, bool bNorm)
{
#define _ADD_GVT_PROC1( _proc, _to_type, _from_type ) \
			& _proc< _to_type, _from_type >

#define _ADD_GVT_PROC2( _proc, _to_type ) \
			nullptr, \
			_ADD_GVT_PROC1( _proc, _to_type, int8    ), \
			_ADD_GVT_PROC1( _proc, _to_type, uint8   ), \
			_ADD_GVT_PROC1( _proc, _to_type, int16   ), \
			_ADD_GVT_PROC1( _proc, _to_type, uint16  ), \
			_ADD_GVT_PROC1( _proc, _to_type, int32   ), \
			_ADD_GVT_PROC1( _proc, _to_type, uint32  ), \
			_ADD_GVT_PROC1( _proc, _to_type, half_t  ), \
			_ADD_GVT_PROC1( _proc, _to_type, float   ), \
			_ADD_GVT_PROC1( _proc, _to_type, double  ), \
			_ADD_GVT_PROC1( _proc, _to_type, fixed_t ), \
			nullptr, nullptr, nullptr, nullptr, nullptr

#define _ADD_EMPTY_PROCS() \
			nullptr, nullptr, nullptr, nullptr, \
			nullptr, nullptr, nullptr, nullptr, \
			nullptr, nullptr, nullptr, nullptr, \
			nullptr, nullptr, nullptr, nullptr

#define _ADD_GVT_PROCS( _proc ) \
			_ADD_EMPTY_PROCS(), \
			_ADD_GVT_PROC2( _proc, int8    ), \
			_ADD_GVT_PROC2( _proc, uint8   ), \
			_ADD_GVT_PROC2( _proc, int16   ), \
			_ADD_GVT_PROC2( _proc, uint16  ), \
			_ADD_GVT_PROC2( _proc, int32   ), \
			_ADD_GVT_PROC2( _proc, uint32  ), \
			_ADD_GVT_PROC2( _proc, half_t  ), \
			_ADD_GVT_PROC2( _proc, float   ), \
			_ADD_GVT_PROC2( _proc, double  ), \
			_ADD_GVT_PROC2( _proc, fixed_t ), \
			_ADD_EMPTY_PROCS(), \
			_ADD_EMPTY_PROCS(), \
			_ADD_EMPTY_PROCS(), \
			_ADD_EMPTY_PROCS(), \
			_ADD_EMPTY_PROCS()


	static PChangeVertTypeProc_t	a_procs[] = {
			_ADD_GVT_PROCS( ChangeVertType ),
			_ADD_GVT_PROCS( ChangeVertTypeNorm ),
			_ADD_GVT_PROCS( ChangeVertTypeScale ),
			_ADD_GVT_PROCS( ChangeVertTypeScaleNorm )
		};

	static const uint	u_count = COUNT_OF( a_procs );

	STATIC_ASSERTE( u_count == 16*16*4 );

	const uint			u_index	= GetVertexTypeIndex( eFrom ) + (GetVertexTypeIndex( eTo ) << 4);

	if ( u_index >= u_count ) {
		WARNING( "invalid index" );
		return nullptr;
	}

	return a_procs[ u_index ];

#undef  _ADD_GVT_PROC1
#undef  _ADD_GVT_PROC2
#undef  _ADD_EMPTY_PROCS
}

/*
=================================================
	
=================================================
*/
bool ChangeSubMeshVertexAttribs(const binary_buffer_t &aVertices, const array<TVertAttribLink> &aAttribLinks,
								uint uVertexCount, binary_buffer_t &aNewVertices, TVerticesAttribs &aAttribs)
{
	struct TVertAttribParams
	{
		PChangeVertTypeProc_t	pProc;
		const void *			pVertices;
		void *					pNewVertices;
		float					fScale;
		uint					uStride,
								uNewSizeOf;
		bool					bNormalize;
		///
		TVertAttribParams():
			pProc(nullptr), pVertices(nullptr), pNewVertices(nullptr),
			uStride(0), uNewSizeOf(0), bNormalize(false), fScale(0.f)
		{}
	};
	typedef array< TVertAttribParams >	vaparams_array_t;


	TVerticesAttribs	a_attribs( aAttribs );
	vaparams_array_t	a_vaparams;
	uint				u_new_vert_size	= 0,
						u_vert_offset	= (uint)aNewVertices.Count(),
						u_new_stride	= 0;

	a_vaparams.Resize( a_attribs.Count(), false );


	for (uint i = 0; i < a_attribs.Count(); ++i)
	{
		bool	b_disable		= false;
		uint	u_size			= GetVertexSize( a_attribs[i].eType ) * a_attribs[i].uCount;
		a_vaparams[i].pVertices = cptr_move( aVertices.ptr(), a_attribs[i].uOffset );
		a_vaparams[i].uStride	= a_attribs[i].uStride == 0 ? u_size : a_attribs[i].uStride;

		for (uint j = 0; j < aAttribLinks.Count(); ++j)
		{
			if ( a_attribs[i].eLocation == aAttribLinks[j].eLocation )
			{
				if ( aAttribLinks[j].bDisabled ) {
					b_disable = true;
					break;
				}

				if ( a_attribs[i].eType != aAttribLinks[j].eType )
				{
					a_vaparams[i].pProc = GetChangeVertTypeProc( aAttribLinks[j].eType, a_attribs[i].eType, aAttribLinks[j].bNormalize, aAttribLinks[j].bScale );

					if ( a_vaparams[i].pProc != nullptr ) {
						a_vaparams[i].fScale	 =  aAttribLinks[j].fScale;
						a_attribs[i].eType		 =  aAttribLinks[j].eType;
						a_vaparams[i].bNormalize =  aAttribLinks[j].bNormalize;
						a_attribs[i].bNormalize  =  aAttribLinks[j].bNormalized;
					}
				}
				break;
			}
		}

		if ( b_disable ) {
			a_attribs.Erase( i );
			--i;
			continue;
		}

		u_size					 = GetVertexSize( a_attribs[i].eType ) * a_attribs[i].uCount;
		a_attribs[i].uOffset	 = u_vert_offset + u_new_vert_size;
		u_new_vert_size			+= u_size;
		a_vaparams[i].uNewSizeOf = u_size;
	}

	u_new_stride = u_new_vert_size;
	aNewVertices.Resize( aNewVertices.Count() + u_new_vert_size * uVertexCount, false );


	for (uint i = 0; i < a_attribs.Count(); ++i)
	{
		a_attribs[i].uStride	= u_new_stride;
		a_vaparams[i].pVertices	= ptr_move( aNewVertices.ptr(), a_attribs[i].uOffset );
	}


	for (uint i = 0; i < uVertexCount; ++i)
	{
		for (uint j = 0; j < a_attribs.Count(); ++j)
		{
			const void * p_from = cptr_move( a_vaparams[j].pVertices, i * a_vaparams[j].uStride );
			void *		 p_to	= ptr_move( a_vaparams[j].pNewVertices, i * u_new_stride );

			if ( a_vaparams[j].pProc != nullptr )
				a_vaparams[j].pProc( p_to, p_from, a_vaparams[j].fScale, a_attribs[j].uCount );
			else
				memcpy( p_to, p_from, a_vaparams[j].uNewSizeOf );
		}
	}

	aAttribs.SetMem( a_attribs );
	return true;
}

/*
=================================================
	
=================================================
*/
template <typename I>
uint GetVerticesCount(const I *pIndices, uint uCount)
{
	if ( uCount == 0 )
		return 0;

	I	u_max_vert = pIndices[0];

	for (uint i = 0; i < uCount; ++i)
		if ( u_max_vert < pIndices[i] )
			u_max_vert = pIndices[i];

	return u_max_vert;
}

/*
=================================================
	
=================================================
*/
bool GetVertexValueType(E_VERTEX_VALUE_TYPE eValue, TVertAttribLink &sLink)
{
	if ( EnumNoCmp( eValue, VT_DISABLE ) )
	{
		switch ( eValue & VT_TYPE_MASK )
		{
			case VT_FLOAT_16	:	sLink.eType = gl_vertex::HALF;		break;
			case VT_FLOAT_32	:	sLink.eType = gl_vertex::FLOAT;		break;
			case VT_FLOAT_64	:	sLink.eType = gl_vertex::DOUBLE;	break;
			case VT_FIXED_32	:	sLink.eType = gl_vertex::FIXED;		break;
			case VT_INT_8		:	sLink.eType = gl_vertex::BYTE;		break;
			case VT_INT_16		:	sLink.eType = gl_vertex::SHORT;		break;
			case VT_INT_32		:	sLink.eType = gl_vertex::INT;		break;
			case VT_UINT_8		:	sLink.eType = gl_vertex::UBYTE;		break;
			case VT_UINT_16		:	sLink.eType = gl_vertex::USHORT;	break;
			case VT_UINT_32		:	sLink.eType = gl_vertex::UINT;		break;
			default				:	return false;
		};
	}
	else
		sLink.bDisabled = true;

	sLink.bNormalize	= EnumCmp( eValue, VT_NORMALIZE );
	sLink.bNormalized	= EnumCmp( eValue, VT_NORMALIZED );
	sLink.bScale		= EnumCmp( eValue, VT_SCALE );
	return true;
}

/*
=================================================
	
=================================================
*/
bool ChangeMultiMeshVertexAttribs(TMultiMesh &sMesh, const array<TVertAttribLink> &aAttribLinks)
{
	uint	u_vert_count = 0;

	binary_buffer_t		a_new_vertices;

	a_new_vertices.Reserve( sMesh.aVertices.Count() );


	for (uint i = 0; i < sMesh.aSubMeshes.Count(); ++i)
	{
		TMultiMesh::TSubMesh	&	s_submesh = sMesh.aSubMeshes[i];
		const void	*			p_indices = cptr_move( sMesh.aIndices.ptr(), s_submesh.uIdxOffset );

		switch ( s_submesh.eType )
		{
			case gl_index::UBYTE :
				u_vert_count = GetVerticesCount( (const uint8 *)p_indices, s_submesh.uIdxOffset );
				break;

			case gl_index::USHORT :
				u_vert_count = GetVerticesCount( (const uint16 *)p_indices, s_submesh.uIdxOffset );
				break;

			case gl_index::UINT :
				u_vert_count = GetVerticesCount( (const uint32 *)p_indices, s_submesh.uIdxOffset );
				break;

			default :
				return false;
		};

		if ( !ChangeSubMeshVertexAttribs( sMesh.aVertices, aAttribLinks, u_vert_count,
										  a_new_vertices, s_submesh.aAttribs ) )
		{
			return false;
		}
	}

	sMesh.aVertices.SetMem( a_new_vertices );
	return true;
}

/*
=================================================
	
=================================================
*/
bool ChangeSceletalMeshVertexAttribs(TSceletalMesh &sMesh, const array<TVertAttribLink> &aAttribLinks)
{
	return false;
}

/*
=================================================
	
=================================================
*/
bool ChangeVertexAttribs(TMesh &sMesh, const array<TMeshVertices> &aAttribs)
{
	array<TVertAttribLink>	a_attrib_links;

	for (uint i = 0; i < aAttribs.Count(); ++i)
	{
		TVertAttribLink		s_link;

		if ( !GetVertexValueType( aAttribs[i].eValue, s_link ) )
			return false;
		
		s_link.eLocation = (e_vertex_attrib::type)aAttribs[i].eAttrib;
		s_link.fScale	 = aAttribs[i].fScale;
		a_attrib_links.PushBack( s_link );
	}


	switch ( sMesh.eType )
	{
		case TMesh::MULTI_MESH :
			return ChangeMultiMeshVertexAttribs( *sMesh.sData.pMesh, a_attrib_links );

		case TMesh::SCELETAL_MESH :
			return ChangeSceletalMeshVertexAttribs( *sMesh.sData.pScelet, a_attrib_links );
	};

	return false;
}



//------------------ Bounding Box Calculation ---------------------//
/*
=================================================
	
=================================================
*/
template <typename T>
bool CalcBBox(const void *pVertices, uint uCount, uint uStride, aabb_f &sBBox)
{
	TAABBox<T>		s_bbox;

	s_bbox.Min() = TVec<T,3>( (const T *)pVertices );
	s_bbox.Max() = s_bbox.Min();

	if ( uStride == 0 )
		uStride += sizeof(T)*3;


	for (uint i = 0; i < uCount; ++i)
	{
		const T	*	p_vert = cptr_move<const T *>( pVertices, i*uStride );

		if ( p_vert[0] < s_bbox.Min().x )	s_bbox.Min().x = p_vert[0];
		if ( p_vert[1] < s_bbox.Min().y )	s_bbox.Min().y = p_vert[1];
		if ( p_vert[2] < s_bbox.Min().z )	s_bbox.Min().z = p_vert[2];

		if ( p_vert[0] > s_bbox.Max().x )	s_bbox.Max().x = p_vert[0];
		if ( p_vert[1] > s_bbox.Max().y )	s_bbox.Max().y = p_vert[1];
		if ( p_vert[2] > s_bbox.Max().z )	s_bbox.Max().z = p_vert[2];
	}

	sBBox = s_bbox.Convert<float>();
	return true;
}

/*
=================================================
	
=================================================
*/
bool CalculateBBox(const void *pVertices, gl_vertex::type eType, uint uCount, uint uStride, aabb_f &sBBox)
{
	switch ( eType )
	{
		case gl_vertex::BYTE	:	return CalcBBox< int8    >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::UBYTE	:	return CalcBBox< uint8   >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::SHORT	:	return CalcBBox< int16   >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::USHORT	:	return CalcBBox< uint16  >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::INT		:	return CalcBBox< int32   >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::UINT	:	return CalcBBox< uint32  >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::HALF	:	return CalcBBox< half_t  >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::FLOAT	:	return CalcBBox< float   >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::DOUBLE	:	return CalcBBox< double  >( pVertices, uCount, uStride, sBBox );
		case gl_vertex::FIXED	:	return CalcBBox< fixed_t >( pVertices, uCount, uStride, sBBox );
		default					:	return false;
	};
}

/*
=================================================
	
=================================================
*/
bool CalculateMultiMeshBBoxes(TMultiMesh &sMesh)
{
	for (uint i = 0; i < sMesh.aSubMeshes.Count(); ++i)
	{
		TVertexAttrib const *	p_attrib  = nullptr;
		TMultiMesh::TSubMesh &	s_submesh = sMesh.aSubMeshes[i];

		for (uint j = 0; j < s_submesh.aAttribs.Count(); ++j)
		{
			if ( s_submesh.aAttribs[j].eLocation == e_vertex_attrib::POSITION &&
				 s_submesh.aAttribs[j].uCount == 3 )
			{
				p_attrib = & s_submesh.aAttribs[j];
				break;
			}
		}

		if ( p_attrib == nullptr )
			return false;

		const void *	p_vertices	 = cptr_move( sMesh.aVertices.ptr(), p_attrib->uOffset );
		const void	*	p_indices	 = cptr_move( sMesh.aIndices.ptr(), s_submesh.uIdxOffset );
		uint			u_vert_count = 0;
		
		switch ( s_submesh.eType )
		{
			case gl_index::UBYTE :
				u_vert_count = GetVerticesCount( (const uint8 *)p_indices, s_submesh.uIdxOffset );
				break;

			case gl_index::USHORT :
				u_vert_count = GetVerticesCount( (const uint16 *)p_indices, s_submesh.uIdxOffset );
				break;

			case gl_index::UINT :
				u_vert_count = GetVerticesCount( (const uint32 *)p_indices, s_submesh.uIdxOffset );
				break;

			default :
				return false;
		};

		if ( !CalculateBBox( p_vertices, p_attrib->eType, u_vert_count, p_attrib->uStride, s_submesh.sBBox ) )
			return false;
	}

	return true;
}

/*
=================================================
	
=================================================
*/
bool CalculateBBoxes(TMesh &sMesh)
{
	switch ( sMesh.eType )
	{
		case TMesh::MULTI_MESH :
			return CalculateMultiMeshBBoxes( *sMesh.sData.pMesh );

		case TMesh::SCELETAL_MESH :
			return false;
	};
	return false;
}

/*
=================================================
	
=================================================
*/
inline void CaclSceletalVertex(uint uFirstWeight, uint uWeightsCount, const array<TSceletalMesh::TWeight> &aWeights,
								const TSceletalAnimData::joint_array_t &aJoints, vec3 &vVertex)
{
	for (uint i = uFirstWeight; i < uWeightsCount; ++i)
	{
		const TSceletalMesh::TWeight	 &	s_weight = aWeights[i];
		const TSceletalAnimData::TJoint &	s_joint  = aJoints[ s_weight.uJointIndex ];

		vVertex += (s_joint.vOrient * s_weight.vPos + s_joint.vPos) * s_weight.fBias;
	}
}

/*
=================================================
	
=================================================
*/
bool CalculateSubMeshBBox(const TSceletalMesh &sMesh, uint uSubMesh, const TSceletalAnimData &sAnim,
						  uint uTrack, uint uFrame, aabb_f &sBBox)
{
	float_array_t					a_vertices;
	uint							u_vert_count = 0;
	TSceletalMesh::TSubMesh	const &	s_submesh = sMesh.aSubMeshes[uSubMesh];
	const void	*					p_indices = cptr_move( sMesh.aIndices.ptr(), s_submesh.uIdxOffset );
	uint							u_pos_attrib = -1;
	
	// find weights vertex attrib
	for (uint i = 0; i < s_submesh.aAttribs.Count(); ++i)
		if ( s_submesh.aAttribs[i].eLocation == e_vertex_attrib::WEIGHTS && s_submesh.aAttribs[i].uCount == 2 )
		{
			u_pos_attrib = i;
			break;
		}

	if ( u_pos_attrib == -1 )
		return false;


	// get vertices count
	switch ( s_submesh.eType )
	{
		case gl_index::UBYTE :
			u_vert_count = GetVerticesCount( (const uint8 *)p_indices, s_submesh.uIdxOffset );
			break;

		case gl_index::USHORT :
			u_vert_count = GetVerticesCount( (const uint16 *)p_indices, s_submesh.uIdxOffset );
			break;

		case gl_index::UINT :
			u_vert_count = GetVerticesCount( (const uint32 *)p_indices, s_submesh.uIdxOffset );
			break;

		default :
			return false;
	};


	TVertexAttrib const &	s_attrib	= s_submesh.aAttribs[u_pos_attrib];
	const void *			p_vertices	= cptr_move( sMesh.aVertices.ptr(), s_attrib.uOffset );
	const uint				u_stride	= s_attrib.uStride == 0 ? GetVertexSize( s_attrib.eType ) * s_attrib.uCount : s_attrib.uStride;


	if ( s_attrib.eType != gl_vertex::UINT && s_attrib.eType != gl_vertex::INT )
	{
		PChangeVertTypeProc_t	p_proc = GetChangeVertTypeProc( gl_vertex::UINT, s_attrib.eType, false, false );

		if ( p_proc == nullptr )
			return false;

		for (uint i = 0; i < u_vert_count; ++i)
		{
			const void *	p_vert		= cptr_move( p_vertices, u_stride*i );
			uint			u_vert[2]	= {0};

			p_proc( u_vert, p_vert, 1.f, 2 );
			
			vec3	v;
			CaclSceletalVertex( u_vert[0], u_vert[1], sMesh.aWeights, sAnim.aJoints, v );

			a_vertices.Append( v.ptr(), 3 );
		}
	}
	else
	{
		for (uint i = 0; i < u_vert_count; ++i)
		{
			const uint32 *	p_u32_verts = cptr_move<const uint32 *>( p_vertices, u_stride*i );

			vec3	v;
			CaclSceletalVertex( p_u32_verts[0], p_u32_verts[1], sMesh.aWeights, sAnim.aJoints, v );
			
			a_vertices.Append( v.ptr(), 3 );
		}
	}

	return CalcBBox<float>( a_vertices.ptr(), u_vert_count, 0, sBBox );
}

/*
=================================================
	
=================================================
*/
bool CalculateTrackBBoxes(const TSceletalMesh &sMesh, TSceletalAnimData &sAnim)
{
	if ( sMesh.uJointsPerFrame != sAnim.uJointPerFrame )
		return false;

	sAnim.aBBoxes.ClearMem();


	for (uint i = 0; i < sAnim.aTracks.Count(); ++i)
	{
		for (uint j = 0; j < sAnim.aTracks[i].uFramesCount; ++j)
		{
			aabb_f	s_bbox;

			for (uint k = 0; k < sMesh.aSubMeshes.Count(); ++k)
			{
				aabb_f	s_tmp_bbox;

				if ( !CalculateSubMeshBBox( sMesh, k, sAnim, i,
						sAnim.aTrackFrames[ sAnim.aTracks[i].uFirstFrame + j ], s_tmp_bbox ) )
				{
					return false;
				}

				s_bbox.Add( s_tmp_bbox );
			}

			sAnim.aBBoxes.PushBack( s_bbox );
		}
	}

	return true;
}
